/********************************************************************************
  * @file    editortabwidget.cpp
  * @author  Lun Li
  * @version V2.2.0
  * @date    2022.7.8
  * @brief   This file provides all EditorTabWidget functions.
  ******************************************************************************/

#include "editortabwidget.h"

#include <QFileDialog>
#include <QGuiApplication>
#include <QMessageBox>
#include <QSaveFile>
#include <QTabBar>

#include "signalconnection.h"

EditorTabWidget::EditorTabWidget(ProgramConfig * const config, QWidget *parent)
    : QTabWidget(parent),
      m_config(config),
      m_findWidget(new FindWidget(this))
{
    initializeActionList();

    setTabPosition(QTabWidget::South);
    setTabsClosable(true);
    connect(this, &QTabWidget::currentChanged, this, &EditorTabWidget::on_currentChanged);
    connect(tabBar(), &QTabBar::tabMoved, this, &EditorTabWidget::on_tabMoved);
    connect(this, &QTabWidget::tabCloseRequested, this, &EditorTabWidget::on_tabCloseRequested);

    extern SignalConnection globalSignal;
    connect(&globalSignal, &SignalConnection::fileNeedOpened, this, &EditorTabWidget::openSourceFile);
    connect(&globalSignal, &SignalConnection::fileNeedClosed, this, &EditorTabWidget::closeSourceFile);
    connect(&globalSignal, &SignalConnection::fileNeedSaved, this, [=](Source *file) {
        saveSourceFile(file);
    });
    connect(&globalSignal, &SignalConnection::showErrorLineRequested, this, [=](Source *file, int lineNumber) {
        openSourceFile(file);
        CodeEditor *editor = widget(file->indexInEditor());
        QTextCursor cursor = editor->textCursor();
        cursor.setPosition(editor->document()->findBlockByLineNumber(lineNumber).position() - 1, QTextCursor::MoveAnchor);
        editor->setTextCursor(cursor);
    });
    connect(this, &EditorTabWidget::cursorPositionChanged, &globalSignal, &SignalConnection::cursorPositionChanged);

}

void EditorTabWidget::openSourceFile(Source *file)
{
    if (!file) {
        return;
    }
    if (file->indexInEditor() >= 0) {
        setCurrentIndex(file->indexInEditor());
        return;
    }
    CodeEditor *editor = new CodeEditor(m_config, file, contextMenu, this);

    if (!file->isNewFile()) {
        if (!file->open(QFile::ReadOnly | QFile::Text)) {
            QMessageBox::critical(this, tr("Error"), tr("Cannot open file \"%1\" for reading.").arg(file->filePath()));
            editor->deleteLater();
            editor = nullptr;
            return;
        } else {
            QTextStream ts(file);
            ts.setEncoding(QStringConverter::Utf8);
            QGuiApplication::setOverrideCursor(Qt::WaitCursor);
            editor->setPlainText(ts.readAll());
            QGuiApplication::restoreOverrideCursor();
            file->close();
        }
    }

    addTab(editor, file->fileName());
    file->setIndexInEditor(count() - 1);
    setCurrentIndex(count() - 1);

    connect(editor, &CodeEditor::modificationChanged, this, [=](bool changed) {
       m_actionList[SaveFile]->setEnabled(changed);
       bool saveAllEnabled = changed;
       int cnt = count();
       for (int i = 0; i < cnt; ++i) {
           saveAllEnabled |= widget(i)->document()->isModified();
       }
       m_actionList[SaveAllFile]->setEnabled(saveAllEnabled);
       setTabText(editor->file()->indexInEditor(), editor->file()->fileName() + (changed ? "*" : ""));
    });
    connect(editor, &CodeEditor::cursorPositionChanged, this, [=]() {
        emit cursorPositionChanged(editor->textCursor().blockNumber() + 1,
                                   editor->textCursor().columnNumber() + 1);
    });
    connect(editor, &CodeEditor::redoAvailable, m_actionList[Redo], &QAction::setEnabled);
    connect(editor, &CodeEditor::undoAvailable, m_actionList[Undo], &QAction::setEnabled);
    connect(editor, &CodeEditor::copyAvailable, m_actionList[Cut], &QAction::setEnabled);
    connect(editor, &CodeEditor::copyAvailable, m_actionList[Copy], &QAction::setEnabled);
    connect(editor, &CodeEditor::copyAvailable, m_actionList[Delete], &QAction::setEnabled);
    connect(editor, &CodeEditor::focusChanged, m_actionList[Paste], &QAction::setEnabled);
    connect(editor, &CodeEditor::focusChanged, m_actionList[SelectAll], &QAction::setEnabled);
}

void EditorTabWidget::closeSourceFile(Source *file)
{
    if (!file) {
        return;
    }
    int index = file->indexInEditor();
    if (index < 0) {
        return;
    }
    CodeEditor *editor = widget(index);
    if (editor->document()->isModified()) {
        int result = QMessageBox::question(this, tr("Warning"),
                                              tr("File: %1 has been modified. Save changes?").arg(file->fileName()),
                                              QMessageBox::Save | QMessageBox::No | QMessageBox::Cancel,
                                              QMessageBox::Save);
        if (result == QMessageBox::Save) {
            saveSourceFile(file);
            if (editor->document()->isModified()) {
                return;
            }
        } else if (result == QMessageBox::Cancel) {
            return;
        }
    }

    file->setIndexInEditor(-1);
    int cnt = count();
    for (int i = index + 1; i < cnt; ++i) {
        on_tabMoved(i, i - 1);
    }
    removeTab(index);
    editor->deleteLater();
}

void EditorTabWidget::saveSourceFile(Source *file, const QString &content)
{
    if (!file) {
        return;
    }
    if (file->indexInEditor() < 0) {
        return;
    }
    QString errorMessage;

    QGuiApplication::setOverrideCursor(Qt::WaitCursor);
    QSaveFile saveFile(file->filePath());
    if (saveFile.open(QFile::WriteOnly | QFile::Text)) {
        QTextStream out(&saveFile);
        out.setEncoding(QStringConverter::Utf8);
        if (content.isEmpty()) {
            out << widget(file->indexInEditor())->toPlainText();
        } else {
            out << content;
        }
        if (!saveFile.commit()) {
            errorMessage = tr("Cannot write file %1:\n%2.")
                           .arg(QDir::toNativeSeparators(file->filePath()), saveFile.errorString());
        }
    } else {
        errorMessage = tr("Cannot open file %1 for writing:\n%2.")
                       .arg(QDir::toNativeSeparators(file->filePath()), saveFile.errorString());
    }
    QGuiApplication::restoreOverrideCursor();

    if (!errorMessage.isEmpty()) {
        QMessageBox::critical(this, tr("Error"), errorMessage);
        return;
    }
    widget(file->indexInEditor())->document()->setModified(false);
}

void EditorTabWidget::on_newFileButtonClicked()
{
    Source *file = new Source(tr("untitled_%1").arg(++newFileCount), true, this);
    openSourceFile(file);
}

void EditorTabWidget::on_openFileButtonClicked()
{
    QStringList paths = QFileDialog::getOpenFileNames(this, tr("Open Files"), m_config->lastOpenPath());
    if (paths.isEmpty()) {
        return;
    }
    m_config->setLastOpenPath(paths.at(0));
    for (const QString &path : paths) {
        Source *file = new Source(path, false, this);
        openSourceFile(file);
    }
}

void EditorTabWidget::on_closeFileButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    closeSourceFile(widget(currentIndex())->file());
}

void EditorTabWidget::on_saveFileButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    saveSourceFile(widget(currentIndex())->file());
}

void EditorTabWidget::on_saveAsFileButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    QString anotherPath = QFileDialog::getSaveFileName(this, tr("Save File"), m_config->lastOpenPath());
    if (anotherPath.isEmpty()) {
        return;
    }
    m_config->setLastOpenPath(anotherPath);
    QString content = widget(currentIndex())->toPlainText();
    Source *file = new Source(anotherPath, false, this);
    openSourceFile(file);
    saveSourceFile(file, content);
}

void EditorTabWidget::on_saveAllFileButtonClicked()
{
    int fileCount = count();
    for (int i = 0; i < fileCount; ++i) {
        saveSourceFile(widget(i)->file());
    }
}

void EditorTabWidget::on_cutButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    widget(currentIndex())->cut();
}

void EditorTabWidget::on_copyButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    widget(currentIndex())->copy();
}

void EditorTabWidget::on_pasteButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    widget(currentIndex())->paste();
}

void EditorTabWidget::on_undoButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    widget(currentIndex())->undo();
}

void EditorTabWidget::on_redoButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    widget(currentIndex())->redo();
}

void EditorTabWidget::on_deleteButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    widget(currentIndex())->textCursor().removeSelectedText();
}

void EditorTabWidget::on_selectAllButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    widget(currentIndex())->selectAll();
}

void EditorTabWidget::on_findAndReplaceButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    m_findWidget->show();
}

void EditorTabWidget::on_zoomInButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    widget(currentIndex())->zoomIn();
}

void EditorTabWidget::on_zoomOutButtonClicked()
{
    if (currentIndex() < 0) {
        return;
    }
    widget(currentIndex())->zoomOut();
}

FindWidget *EditorTabWidget::findWidget() const
{
    return m_findWidget;
}

void EditorTabWidget::initializeActionList()
{
    m_actionList.resize(ZoomOut + 1);

    m_actionList[NewFile] = new QAction(QIcon(":/icon/NewFile.png"), tr("New File"), this);
    m_actionList[NewFile]->setShortcut(QKeySequence("Ctrl+N"));
    connect(m_actionList[NewFile], &QAction::triggered, this, &EditorTabWidget::on_newFileButtonClicked);

    m_actionList[OpenFile] = new QAction(QIcon(":/icon/OpenFile.png"), tr("Open File"), this);
    m_actionList[OpenFile]->setShortcut(QKeySequence("Ctrl+O"));
    connect(m_actionList[OpenFile], &QAction::triggered, this, &EditorTabWidget::on_openFileButtonClicked);

    m_actionList[CloseFile] = new QAction(QIcon(":/icon/CloseFile.png"), tr("Close File"), this);
    connect(m_actionList[CloseFile], &QAction::triggered, this, &EditorTabWidget::on_closeFileButtonClicked);

    m_actionList[SaveFile] = new QAction(QIcon(":/icon/SaveFile.png"), tr("Save File"), this);
    m_actionList[SaveFile]->setShortcut(QKeySequence("Ctrl+S"));
    connect(m_actionList[SaveFile], &QAction::triggered, this, &EditorTabWidget::on_saveFileButtonClicked);

    m_actionList[SaveAsFile] = new QAction(QIcon(":/icon/SaveAsFile.png"), tr("Save As File"), this);
    connect(m_actionList[SaveAsFile], &QAction::triggered, this, &EditorTabWidget::on_saveAsFileButtonClicked);

    m_actionList[SaveAllFile] = new QAction(tr("Save All File"), this);
    m_actionList[SaveAllFile]->setShortcut(QKeySequence("Ctrl+Shift+S"));
    connect(m_actionList[SaveAllFile], &QAction::triggered, this, &EditorTabWidget::on_saveAllFileButtonClicked);

    m_actionList[Cut] = new QAction(QIcon(":/icon/Cut.png"), tr("Cut"), this);
    m_actionList[Cut]->setShortcut(QKeySequence("Ctrl+X"));
    connect(m_actionList[Cut], &QAction::triggered, this, &EditorTabWidget::on_cutButtonClicked);

    m_actionList[Copy] = new QAction(QIcon(":/icon/Copy.png"), tr("Copy"), this);
    m_actionList[Copy]->setShortcut(QKeySequence("Ctrl+C"));
    connect(m_actionList[Copy], &QAction::triggered, this, &EditorTabWidget::on_copyButtonClicked);

    m_actionList[Paste] = new QAction(QIcon(":/icon/Paste.png"), tr("Paste"), this);
    m_actionList[Paste]->setShortcut(QKeySequence("Ctrl+V"));
    connect(m_actionList[Paste], &QAction::triggered, this, &EditorTabWidget::on_pasteButtonClicked);

    m_actionList[Undo] = new QAction(QIcon(":/icon/Undo.png"), tr("Undo"), this);
    m_actionList[Undo]->setShortcut(QKeySequence("Ctrl+Z"));
    connect(m_actionList[Undo], &QAction::triggered, this, &EditorTabWidget::on_undoButtonClicked);

    m_actionList[Redo] = new QAction(QIcon(":/icon/Redo.png"), tr("Redo"), this);
    m_actionList[Redo]->setShortcut(QKeySequence("Ctrl+Shift+Z"));
    connect(m_actionList[Redo], &QAction::triggered, this, &EditorTabWidget::on_redoButtonClicked);

    m_actionList[Delete] = new QAction(tr("Delete"), this);
    m_actionList[Delete]->setShortcut(QKeySequence("Delete"));
    connect(m_actionList[Delete], &QAction::triggered, this, &EditorTabWidget::on_deleteButtonClicked);

    m_actionList[SelectAll] = new QAction(QIcon(":/icon/SelectAll.png"), tr("Select All"), this);
    m_actionList[SelectAll]->setShortcut(QKeySequence("Ctrl+A"));
    connect(m_actionList[SelectAll], &QAction::triggered, this, &EditorTabWidget::on_selectAllButtonClicked);

    m_actionList[FindAndReplace] = new QAction(QIcon(":/icon/Find.png"), tr("Find && Replace"), this);
    m_actionList[FindAndReplace]->setShortcut(QKeySequence("Ctrl+F"));
    connect(m_actionList[FindAndReplace], &QAction::triggered, this, &EditorTabWidget::on_findAndReplaceButtonClicked);

    m_actionList[ZoomIn] = new QAction(QIcon(":/icon/ZoomIn.png"), tr("Zoom In"), this);
    m_actionList[ZoomIn]->setShortcut(QKeySequence("Ctrl+Shift++"));
    connect(m_actionList[ZoomIn], &QAction::triggered, this, &EditorTabWidget::on_zoomInButtonClicked);

    m_actionList[ZoomOut] = new QAction(QIcon(":/icon/ZoomOut.png"), tr("Zoom Out"), this);
    m_actionList[ZoomOut]->setShortcut(QKeySequence("Ctrl+Shift+-"));
    connect(m_actionList[ZoomOut], &QAction::triggered, this, &EditorTabWidget::on_zoomOutButtonClicked);

    editorActionTeam << m_actionList[CloseFile]
                      << m_actionList[SaveFile]
                      << m_actionList[SaveAsFile]
                      << m_actionList[SaveAllFile]
                      << m_actionList[Cut]
                      << m_actionList[Copy]
                      << m_actionList[Paste]
                      << m_actionList[Undo]
                      << m_actionList[Redo]
                      << m_actionList[Delete]
                      << m_actionList[SelectAll]
                      << m_actionList[FindAndReplace]
                      << m_actionList[ZoomIn]
                      << m_actionList[ZoomOut];
    editorActionTeam.setEnabled(false);

    contextMenu = new QMenu(this);
    contextMenu->addAction(m_actionList[Cut]);
    contextMenu->addAction(m_actionList[Copy]);
    contextMenu->addAction(m_actionList[Paste]);
    contextMenu->addAction(m_actionList[Undo]);
    contextMenu->addAction(m_actionList[Redo]);
    contextMenu->addAction(m_actionList[Delete]);
    contextMenu->addAction(m_actionList[SelectAll]);
    contextMenu->addAction(m_actionList[FindAndReplace]);
    contextMenu->addAction(m_actionList[ZoomIn]);
    contextMenu->addAction(m_actionList[ZoomOut]);
}

void EditorTabWidget::on_currentChanged(int index)
{
    if (index < 0) {
        editorActionTeam.setEnabled(false);
        m_findWidget->hide();
        return;
    }
    bool keepEnabled = m_actionList[SaveAllFile]->isEnabled();
    editorActionTeam.setEnabled(true);
    m_actionList[SaveFile]->setEnabled(widget(index)->document()->isModified());
    m_actionList[SaveAllFile]->setEnabled(keepEnabled);
    m_actionList[Cut]->setEnabled(widget(index)->textCursor().hasSelection());
    m_actionList[Copy]->setEnabled(widget(index)->textCursor().hasSelection());
    m_actionList[Paste]->setEnabled(widget(index)->hasFocus() && widget(index)->canPaste());
    m_actionList[Undo]->setEnabled(widget(index)->document()->isUndoAvailable());
    m_actionList[Redo]->setEnabled(widget(index)->document()->isRedoAvailable());
    m_actionList[Delete]->setEnabled(widget(index)->textCursor().hasSelection());
    m_actionList[SelectAll]->setEnabled(widget(index)->hasFocus());
    emit cursorPositionChanged(widget(index)->textCursor().blockNumber() + 1,
                               widget(index)->textCursor().columnNumber() + 1);
}

void EditorTabWidget::on_tabMoved(int from, int to)
{
    widget(from)->file()->setIndexInEditor(to);
}

void EditorTabWidget::on_tabCloseRequested(int index)
{
    if (index < 0) {
        return;
    }
    closeSourceFile(widget(index)->file());
}

const QList<QAction *> &EditorTabWidget::actionList() const
{
    return m_actionList;
}

CodeEditor *EditorTabWidget::widget(int index) const
{
    return (CodeEditor *)(QTabWidget::widget(index));
}


FindWidget::FindWidget(EditorTabWidget *editorTabWidget)
    : QWidget(editorTabWidget),
      editorTabWidget(editorTabWidget)
{
    hide();

    QLabel *findLabel = new QLabel(tr("Find:"));
    expInput = new QLineEdit;
    findPreviousButton = new QPushButton(tr("Find Previous"));
    findNextButton = new QPushButton(tr("Find Next"));
    caseCheckBox = new QCheckBox(tr("Case sensitive"));
    regexpCheckBox = new QCheckBox(tr("Use Regular Expressions"));
    QPushButton *closeButton = new QPushButton(tr("Close"));
    QWidget *findWidget = new QWidget;
    findWidget->setLayout(new NoMarginHBoxLayout);
    findWidget->layout()->addWidget(findLabel);
    findWidget->layout()->addWidget(expInput);
    findWidget->layout()->addWidget(findPreviousButton);
    findWidget->layout()->addWidget(findNextButton);
    findWidget->layout()->addWidget(caseCheckBox);
    findWidget->layout()->addWidget(regexpCheckBox);
    findWidget->layout()->addWidget(closeButton);

    QLabel *replaceLabel = new QLabel(tr("Replace With:"));
    replaceExpInput = new QLineEdit;
    QPushButton *replaceButton = new QPushButton(tr("Replace"));
    QPushButton *replaceAndFindButton = new QPushButton(tr("Replace and Find"));
    QPushButton *replaceAllButton = new QPushButton(tr("Replace All"));
    QWidget *replaceWidget = new QWidget;
    replaceWidget->setLayout(new NoMarginHBoxLayout);
    replaceWidget->layout()->addWidget(replaceLabel);
    replaceWidget->layout()->addWidget(replaceExpInput);
    replaceWidget->layout()->addWidget(replaceButton);
    replaceWidget->layout()->addWidget(replaceAndFindButton);
    replaceWidget->layout()->addWidget(replaceAllButton);

    setLayout(new NoMarginVBoxLayout);
    layout()->addWidget(findWidget);
    layout()->addWidget(replaceWidget);

    connect(findPreviousButton, &QPushButton::clicked, this, [=]() {
        find(FindType::PreviousLoop);
    });
    connect(findNextButton, &QPushButton::clicked, this, [=]() {
        find(FindType::NextLoop);
    });
    connect(closeButton, &QPushButton::clicked, this, &FindWidget::close);
    connect(replaceButton, &QPushButton::clicked, this, &FindWidget::replace);
    connect(replaceAndFindButton, &QPushButton::clicked, this, [=]() {
         replace();
         find(FindType::NextLoop);
    });
    connect(replaceAllButton, &QPushButton::clicked, this, [=]() {
        CodeEditor *codeEditor = editorTabWidget->widget(editorTabWidget->currentIndex());
        codeEditor->moveCursor(QTextCursor::Start);
        while(find(FindType::NextToEnd)) {
            replace();
        }
    });

    connect(expInput, &QLineEdit::textChanged, this, [=](const QString &text) {
        findPreviousButton->setEnabled(!text.isEmpty());
        findNextButton->setEnabled(!text.isEmpty());
        if (!replaceExpInput->text().isEmpty()){
            replaceButton->setEnabled(!text.isEmpty());
            replaceAndFindButton->setEnabled(!text.isEmpty());
            replaceAllButton->setEnabled(!text.isEmpty());
        }
    });

    connect(replaceExpInput, &QLineEdit::textChanged, this, [=](const QString &text) {
        if (!expInput->text().isEmpty()) {
            replaceButton->setEnabled(!text.isEmpty());
            replaceAndFindButton->setEnabled(!text.isEmpty());
            replaceAllButton->setEnabled(!text.isEmpty());
        }
    });

    findPreviousButton->setEnabled(false);
    findNextButton->setEnabled(false);
    replaceButton->setEnabled(false);
    replaceAndFindButton->setEnabled(false);
    replaceAllButton->setEnabled(false);
}

QSize FindWidget::sizeHint() const
{
    return QSize(editorTabWidget->width(), 0);
}

void FindWidget::setExpRed()
{
    QPalette palette;
    palette.setBrush(QPalette::Text, QBrush(Qt::red, Qt::DiagCrossPattern));
    expInput->setPalette(palette);
}

void FindWidget::replace()
{
    if (replaceExpInput->text().isEmpty()) {
        return;
    }

    CodeEditor *codeEditor = editorTabWidget->widget(editorTabWidget->currentIndex());
    if (regexpCheckBox->isChecked()) {
        if (codeEditor->textCursor().hasSelection()) {
            QString str = codeEditor->textCursor().selectedText();
            QRegularExpression re(expInput->text());
            if (!caseCheckBox->isChecked()) {
                re.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
            }
            codeEditor->textCursor().insertText(str.replace(re, replaceExpInput->text()));
        }
    } else {
        if (codeEditor->textCursor().selectedText() == expInput->text()
                || (!caseCheckBox->isChecked() && codeEditor->textCursor().selectedText().toLower() == expInput->text().toLower())) {
            codeEditor->textCursor().insertText(replaceExpInput->text());
        }
    }
}

bool FindWidget::find(FindWidget::FindType type)
{
    if (expInput->text().isEmpty()) {
        return false;
    }

    CodeEditor *codeEditor = editorTabWidget->widget(editorTabWidget->currentIndex());

    QTextDocument::FindFlags flags;
    if (caseCheckBox->isChecked()) {
        flags |= QTextDocument::FindCaseSensitively;
    }
    if (type == PreviousLoop) {
        flags |= QTextDocument::FindBackward;
    }

    if (regexpCheckBox->isChecked()) {
        QRegularExpression exp(expInput->text());
        if (!caseCheckBox->isChecked()) {
            exp.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
        }
        switch (type) {
        case NextToEnd:
            return codeEditor->find(exp, flags);
        case PreviousLoop:
            if (!codeEditor->find(exp, flags)) {
                QTextCursor cursor = codeEditor->textCursor();
                codeEditor->moveCursor(QTextCursor::End);
                if (codeEditor->find(exp, flags)) {
                    return true;
                } else {
                    codeEditor->setTextCursor(cursor);
                    setExpRed();
                    return false;
                }
            } else {
                return true;
            }
        case NextLoop:
            if (!codeEditor->find(exp, flags)) {
                QTextCursor cursor = codeEditor->textCursor();
                codeEditor->moveCursor(QTextCursor::Start);
                if (codeEditor->find(exp, flags)) {
                    return true;
                } else {
                    codeEditor->setTextCursor(cursor);
                    setExpRed();
                    return false;
                }
            } else {
                return true;
            }
        }
    } else {
        QString exp = expInput->text();
        switch (type) {
        case NextToEnd:
            return codeEditor->find(exp, flags);
        case PreviousLoop:
            if (!codeEditor->find(exp, flags)) {
                QTextCursor cursor = codeEditor->textCursor();
                codeEditor->moveCursor(QTextCursor::End);
                if (codeEditor->find(exp, flags)) {
                    return true;
                } else {
                    codeEditor->setTextCursor(cursor);
                    setExpRed();
                    return false;
                }
            } else {
                return true;
            }
        case NextLoop:
            if (!codeEditor->find(exp, flags)) {
                QTextCursor cursor = codeEditor->textCursor();
                codeEditor->moveCursor(QTextCursor::Start);
                if (codeEditor->find(exp, flags)) {
                    return true;
                } else {
                    codeEditor->setTextCursor(cursor);
                    setExpRed();
                    return false;
                }
            } else {
                return true;
            }
        }
    }
    return false;
}

void FindWidget::show()
{
    QWidget::show();
    expInput->setFocus(Qt::ShortcutFocusReason);
}
